package com.chhuang.huaao.handler;

import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import com.alibaba.fastjson.JSONObject;
import com.chhuang.core.holder.SpringContextHolder;
import com.chhuang.huaao.po.HaCustomer;
import com.chhuang.huaao.po.HaMachine;
import com.chhuang.huaao.service.IHaCustomerService;
import com.chhuang.huaao.service.IHaMachineService;
import com.chhuang.huaao.thread.ClearPushSetThread;
import com.chhuang.huaao.thread.GeTuiThread;
import com.chhuang.utils.Assert;
import com.chhuang.utils.date.DateUtil;

/**
 * 设备服务接口
 * @author CHHUANG
 *
 */
public class ServerHandler extends IoHandlerAdapter {
	
	private static final Log log = LogFactory.getLog(ServerHandler.class);
	
	public static Set<String> lessWaterWarnSet = new CopyOnWriteArraySet<String>();//推送过的缺水报警，半小时内不重复推送
	public static Set<String> lessFuelWarnSet = new CopyOnWriteArraySet<String>();//推送过的缺料报警，半小时内不重复推送
	public static Set<String> hightTempWarnSet = new CopyOnWriteArraySet<String>();//推送过的高温报警，半小时内不重复推送
	
	private IHaMachineService machineService = SpringContextHolder.getBean("haMachineServiceImpl");
	private IHaCustomerService customerService = SpringContextHolder.getBean("haCustomerServiceImpl");
	
	/**
	 * 客户端发送的命令
	 */
	public static final String[] CALLS = {"LOGIN","HEART","STATUS","LESSWATER","LESSFUEL","HIGHTTEMP"};
	
	/**
	 * 服务器发送的命令
	 */
//	public static final String[] ORDERS = {"TURN","QUERY"};
	
	/**
	 * 状态
	 */
	public static final String[] STATUS = {
		"SWITCH_S",
		"BIN_S",
		"STOVE_S",
		"BLOWER_S",
		"WIND_S",
		"PUMP_S",
		"FIRE_S",
		"WATER_T",
		"SMOKE_T",
		"IWATER_T",
		"RUN_S",
		"LESSWATER_W",
		"LESSFUEL_W",
		"HIGHTTEMP_W"
	};
	
	/**
	 * string 表示机器的编号
	 * iosession 就是会话对象
	 */
	public static Map<String, IoSession> sessionMap = new Hashtable<String, IoSession>();
	
	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		super.messageReceived(session, message);
		// 接收客户端的数据
        String command = String.valueOf(message);
        String address = session.getRemoteAddress().toString();
        log.debug("服务端接收到客户端"+address+"发来的信息: " + command);
        
        if(Assert.isValidString(command)){
        	if(command.startsWith(CALLS[0])){//登录
        		login(command, session);
        	}else if(command.startsWith(CALLS[1])){//心跳
        		heart(command, session);
        	}else if(command.startsWith(CALLS[2])){//状态
        		status(command, session);
        	}else if(command.startsWith(CALLS[3])){//缺水报警
        		lessWater(command, session);
        	}else if(command.startsWith(CALLS[4])){//缺料报警
        		lessFuel(command, session);
        	}else if(command.startsWith(CALLS[5])){//高温报警
        		hightTemp(command, session);
        	}else{//不是命令
        		session.close(true);session=null;
        	}
        }
        
	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		super.messageSent(session, message);
        String address = session.getRemoteAddress().toString();
		log.debug("服务端发送给客户端"+address+"的信息: " + message.toString());
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		super.sessionCreated(session);
        String address = session.getRemoteAddress().toString();
		log.debug("服务端与客户端"+address+"创建连接...");
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		super.sessionOpened(session);
        String address = session.getRemoteAddress().toString();
		log.debug("服务端与客户端"+address+"连接打开...");
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		super.sessionClosed(session);
        String address = session.getRemoteAddress().toString();
		log.debug("服务端与客户端"+address+"连接关闭...");
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
		super.exceptionCaught(session, cause);
        String address = session.getRemoteAddress().toString();
		log.debug("服务端与客户端"+address+"连接异常: " + cause.getMessage());
        session.close(true);session=null;
	}
	
	@Override
	public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
		super.sessionIdle(session, status);
		int idleCount = session.getIdleCount(status);
        String address = session.getRemoteAddress().toString();
        log.debug("服务端与客户端"+address+"的连接进入空闲状态: " + idleCount);
        if(idleCount > 30){//空闲超过一个月就关闭
	        session.close(true);session=null;
		}
	}
	
	/**
	 * 客户端注册登录
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void login(String command, IoSession session) throws Exception{
		if(Assert.isInvalidString(command) || session==null){
			log.debug("login 参数为空");
			throw new Exception("login 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length!=2){
			log.debug("login 命令不全");
			throw new Exception("login 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("login 编号不全");
			throw new Exception("login 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//保存机器到数据库,包括address和心跳时间
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			Date now = new Date();
			machine.setMachineOnlineStatus(1);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);
			
			//把机器编号和session保存到sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);session=null;
				}
			}
		}else{//设备数据库中不存在，反设备添加到数据库中
			machine = new HaMachine();
			machine.setMachineNum(key);
			machine.setMachineOnlineStatus(1);
			machine.setMachineAddress(address);
			Date now = new Date();
			machine.setCreateTime(now);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machineService.save(machine);
		}
		sessionMap.put(key, session);
	}
	
	/**
	 * 客户端心跳连接
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void heart(String command, IoSession session) throws Exception{
		if(Assert.isInvalidString(command) || session==null){
			log.debug("heart 参数为空");
			throw new Exception("heart 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length!=2){
			log.debug("heart 命令不全");
			throw new Exception("heart 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("heart 编号不全");
			throw new Exception("heart 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//保存机器到数据库,包括address和心跳时间
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			Date now = new Date();
			machine.setMachineOnlineStatus(1);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);
			
			//把机器编号和session保存到sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);session=null;
				}
			}
			sessionMap.put(key, session);
		}else{//设备数据库中不存在，就关闭session
			session.close(true);session=null;
			sessionMap.remove(key);
		}
	}
	
	/**
	 * 客户端返回状态信息
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void status(String command, IoSession session) throws Exception {
		if(Assert.isInvalidString(command) || session==null){
			log.debug("status 参数为空");
			throw new Exception("status 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length<2){
			log.debug("status 命令不全");
			throw new Exception("status 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("status 编号不全");
			throw new Exception("status 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//修改数据库数据
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			for(int i=2; i<infos.length; i++){
				String params[] = infos[i].split(":");
				if(Assert.isInvalidArray(params) || params.length!=2){
					log.debug("status 状态参数不全");
					throw new Exception("status 状态参数不全");
				}
				if(STATUS[0].equals(params[0]) && StringUtils.isNumeric(params[1])){//开关
					machine.setMachineRunStatus(Integer.parseInt(params[1]));
				}else if(STATUS[1].equals(params[0]) && StringUtils.isNumeric(params[1])){//料仓状态
					machine.setMachineBinStatus(Integer.parseInt(params[1]));
				}else if(STATUS[2].equals(params[0]) && StringUtils.isNumeric(params[1])){//炉膛状态
					machine.setMachineStoveStatus(Integer.parseInt(params[1]));
				}else if(STATUS[3].equals(params[0]) && StringUtils.isNumeric(params[1])){//鼓风机状态
					machine.setMachineBlowerStatus(Integer.parseInt(params[1]));
				}else if(STATUS[4].equals(params[0]) && StringUtils.isNumeric(params[1])){//引风机状态
					machine.setMachineWindStatus(Integer.parseInt(params[1]));
				}else if(STATUS[5].equals(params[0]) && StringUtils.isNumeric(params[1])){//水泵状态
					machine.setMachinePumpStatus(Integer.parseInt(params[1]));
				}else if(STATUS[6].equals(params[0]) && StringUtils.isNumeric(params[1])){//点火棒状态
					machine.setMachineFireStatus(Integer.parseInt(params[1]));
				}else if(STATUS[7].equals(params[0]) && isNumber(params[1])){//实时水温
					machine.setMachineWaterTemp(Double.parseDouble(params[1]));
				}else if(STATUS[8].equals(params[0]) && isNumber(params[1])){//烟气温度
					machine.setMachineSmokeTemp(Double.parseDouble(params[1]));
				}else if(STATUS[9].equals(params[0]) && isNumber(params[1])){//进水温度
					machine.setMachineInletWaterTemp(Double.parseDouble(params[1]));
				}else if(STATUS[10].equals(params[0]) && StringUtils.isNumeric(params[1])){//运行阶段
					machine.setMachineRuningStatus(Integer.parseInt(params[1]));
				}else if(STATUS[11].equals(params[0]) && StringUtils.isNumeric(params[1])){//缺水报警
					machine.setMachineLessWaterWarn(toStatus(params[1]));
					if(machine.getMachineLessWaterWarn()!=null && machine.getMachineLessWaterWarn()){
						warnning("缺水报警", machine);
					}
				}else if(STATUS[12].equals(params[0]) && StringUtils.isNumeric(params[1])){//缺料报警
					machine.setMachineLessFuelWarn(toStatus(params[1]));
					if(machine.getMachineLessFuelWarn()!=null && machine.getMachineLessFuelWarn()){
						warnning("缺料报警", machine);
					}
				}else if(STATUS[13].equals(params[0]) && StringUtils.isNumeric(params[1])){//高温报警
					machine.setMachineHightTempWarn(toStatus(params[1]));
					if(machine.getMachineHightTempWarn()!=null && machine.getMachineHightTempWarn()){
						warnning("高温报警", machine);
					}
				}
			}
			
			Date now = new Date();
			machine.setMachineOnlineStatus(1);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);
			
			//把机器编号和session保存到sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);oldSession=null;
				}
			}
			sessionMap.put(key, session);
		}else{//设备数据库中不存在，就关闭session
			session.close(true);session=null;
			sessionMap.remove(key);
		}
		
	}
	
	/**
	 * 缺水报警
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void lessWater(String command, IoSession session) throws Exception {
		if(Assert.isInvalidString(command) || session==null){
			log.debug("lessWater 参数为空");
			throw new Exception("lessWater 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length!=2){
			log.debug("lessWater 命令不全");
			throw new Exception("lessWater 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("lessWater 编号不全");
			throw new Exception("lessWater 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//修改数据库数据
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			Date now = new Date();
			machine.setMachineOnlineStatus(0);
			machine.setMachineLessWaterWarn(true);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);
			
			//推送缺水报警到手机
			warnning("缺水报警", machine);
			
			//修改sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);oldSession=null;
				}
			}
			sessionMap.put(key, session);
		}else{//设备数据库中不存在，就关闭session
			session.close(true);session=null;
			sessionMap.remove(key);
		}
	}
	
	/**
	 * 缺料报警
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void lessFuel(String command, IoSession session) throws Exception {
		if(Assert.isInvalidString(command) || session==null){
			log.debug("lessFuel 参数为空");
			throw new Exception("lessFuel 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length!=2){
			log.debug("lessFuel 命令不全");
			throw new Exception("lessFuel 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("lessFuel 编号不全");
			throw new Exception("lessFuel 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//修改数据库数据
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			Date now = new Date();
			machine.setMachineOnlineStatus(0);
			machine.setMachineLessFuelWarn(true);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);
			
			//推送缺料报警到手机
			warnning("缺料报警", machine);
			
			//修改sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);oldSession=null;
				}
			}
			sessionMap.put(key, session);
		}else{//设备数据库中不存在，就关闭session
			session.close(true);session=null;
			sessionMap.remove(key);
		}
	}

	/**
	 * 高温报警
	 * @param command
	 * @param session
	 * @throws Exception 
	 */
	private void hightTemp(String command, IoSession session) throws Exception {
		if(Assert.isInvalidString(command) || session==null){
			log.debug("hightTemp 参数为空");
			throw new Exception("hightTemp 参数为空");
		}
		String infos[] = command.split(" ");
		if(Assert.isInvalidArray(infos) || infos.length!=2){
			log.debug("hightTemp 命令不全");
			throw new Exception("hightTemp 命令不全");
		}
		String machineNums[] = infos[1].split(":");
		if(Assert.isInvalidArray(machineNums) || !"ID".equals(machineNums[0]) || machineNums.length!=2){
			log.debug("hightTemp 编号不全");
			throw new Exception("hightTemp 编号不全");
		}
		String key = machineNums[1];
		String address = session.getRemoteAddress().toString();
		
		//修改数据库数据
		HaMachine machine = machineService.findMachineByMachineNum(key);
		if(machine!=null){
			Date now = new Date();
			machine.setMachineOnlineStatus(1);
			machine.setMachineHightTempWarn(true);
			machine.setModifyTime(now);
			machine.setHeartTime(now);
			machine.setMachineAddress(address);
			machineService.update(machine);

			//推送高温报警到手机 
			warnning("高温报警", machine);
			
			//修改sessionMap
			//如果内存中机器对就的session还存在并且和当前的session不是同一个，先把原来的session关闭
			IoSession oldSession = sessionMap.get(key);
			if(oldSession!=null){
				String oldAdderss = oldSession.getRemoteAddress().toString();
				if(!address.equals(oldAdderss)){
					oldSession.close(true);oldSession=null;
				}
			}
			sessionMap.put(key, session);
		}else{//设备数据库中不存在，就关闭session
			session.close(true);session=null;
			sessionMap.remove(key);
		}
	}
	
	/**
	 * 是否是数字，包括小数
	 * @param num
	 * @return
	 */
	private boolean isNumber(String num){
		if(StringUtils.isNumeric(num)){
			return true;
		}
		String[] nums = num.split("\\.");
		if(Assert.isInvalidArray(nums) || nums.length>2){
			return false;
		}
		for(String n : nums){
			if(!StringUtils.isNumeric(n)){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 状态类型转换
	 * @param num
	 * @return
	 */
	private Boolean toStatus(String num){
		if("0".equals(num)){
			return false;
		}else if("1".equals(num)){
			return true;
		}else{
			return null;
		}
	}
	
	/**
	 * 推送报警
	 * @param title
	 * @throws Exception 
	 */
	private void warnning(String title, HaMachine machine) throws Exception{
		if("缺水报警".equals(title)){
			if(lessWaterWarnSet.contains(machine.getMachineId())){
				return;
			}else{
				lessWaterWarnSet.add(machine.getMachineId());
				new Thread(new ClearPushSetThread(lessWaterWarnSet, machine.getMachineId())).start();
			}
		}else if("缺料报警".equals(title)){
			if(lessFuelWarnSet.contains(machine.getMachineId())){
				return;
			}else{
				lessFuelWarnSet.add(machine.getMachineId());
				new Thread(new ClearPushSetThread(lessFuelWarnSet, machine.getMachineId())).start();
			}
		}else if("高温报警".equals(title)){
			if(hightTempWarnSet.contains(machine.getMachineId())){
				return;
			}else{
				hightTempWarnSet.add(machine.getMachineId());
				new Thread(new ClearPushSetThread(hightTempWarnSet, machine.getMachineId())).start();
			}
		}
		
		JSONObject message = new JSONObject();
		JSONObject payload = new JSONObject();
		payload.put("action", "warn");
		payload.put("machineId", machine.getMachineId());
		message.put("title", title);
		message.put("content", "设备编号:"+machine.getMachineNum()+"，时间："+DateUtil.date2String(new Date(),DateUtil.YYYY_MM_DD_HH_MM_SS));
		message.put("payload", payload.toString());
		if(Assert.isValidString(machine.getCustomerId())){
			HaCustomer customer = customerService.findByPrimaryKey(machine.getCustomerId());
			if(customer!=null && Assert.isValidString(customer.getClientId())){
				new Thread(new GeTuiThread(customer.getClientId(), message)).start();
			}
		}
		if(Assert.isValidString(machine.getCustomerId1())){
			HaCustomer customer = customerService.findByPrimaryKey(machine.getCustomerId1());
			if(customer!=null && Assert.isValidString(customer.getClientId())){
				new Thread(new GeTuiThread(customer.getClientId(), message)).start();
			}
		}
		if(Assert.isValidString(machine.getCustomerId2())){
			HaCustomer customer = customerService.findByPrimaryKey(machine.getCustomerId2());
			if(customer!=null && Assert.isValidString(customer.getClientId())){
				new Thread(new GeTuiThread(customer.getClientId(), message)).start();
			}
		}
		if(Assert.isValidString(machine.getCustomerId3())){
			HaCustomer customer = customerService.findByPrimaryKey(machine.getCustomerId3());
			if(customer!=null && Assert.isValidString(customer.getClientId())){
				new Thread(new GeTuiThread(customer.getClientId(), message)).start();
			}
		}
	}
	
}
